from datetime import datetime, date, timedelta

from aiogram import Router, types, F
from aiogram.fsm.context import FSMContext
from loguru import logger as log

from tg_bot.config import Config
from tg_bot.filters.user_filter import IsAdmin
from tg_bot.keyboards import inline, callback_factory as cf
from tg_bot.services.text import get_text_for_statistics
from tg_bot.states.admin import AdminCmd, AdminStat

dp = Router()
dp.message.filter(IsAdmin())
dp.callback_query.filter(IsAdmin())


async def get_year_and_month(state: FSMContext) -> datetime.date:
    data = await state.get_data()
    year: int = data.get('year')
    month: int = data.get('month')
    if month is None or year is None:
        today = date.today()
        year = today.year
        month = today.month
    return year, month


@dp.callback_query(cf.AdminMenuCall.filter(
    F.item == 'statistics'
), AdminCmd.main)
async def admin_statistics_call(call: types.CallbackQuery,
                                state: FSMContext):
    current_date: date = date.today()
    text = 'Укажите начало для вывода статистики: '
    key = await inline.concatenate_key(
        await inline.calendar_keyboard(current_date.year, current_date.month),
        await inline.today_stat(),
        await inline.back('admin_menu')
    )
    try:
        await call.message.edit_text(text, reply_markup=key)
    except Exception as e:
        log.warning(e)
        await call.message.answer(text, reply_markup=key)
    await state.set_state(AdminStat.start_date)


@dp.callback_query(cf.AdminActionCall.filter(
    F.action == 'today_stat'
), AdminStat())
async def today_stat(call: types.CallbackQuery,
                     config: Config):
    start_date: datetime = datetime.now()
    if start_date.hour < config.misc.start_work:
        start_date = start_date - timedelta(days=1)
    end_date: date = start_date + timedelta(days=1)
    await answer_statistics_message(
        call.message,
        datetime(start_date.year, start_date.month, start_date.day, config.misc.start_work),
        datetime(end_date.year, end_date.month, end_date.day, config.misc.start_work)
    )


@dp.callback_query(cf.ActionCall.filter(
    F.action.in_({'prev_year', 'next_year', 'next_month', 'prev_month'})
), AdminStat())
async def year_pagination(call: types.CallbackQuery,
                          state: FSMContext,
                          callback_data: cf.ActionCall):
    year, month = await get_year_and_month(state)
    if callback_data.action in ['prev_year', 'next_year']:
        year += 1 if callback_data.action == 'next_year' else -1
    else:
        month += 1 if callback_data.action == 'next_month' else -1
        if month > 12:
            month = 1
            year += 1
        elif month < 1:
            month = 12
            year -= 1
    calendar_key = await inline.calendar_keyboard(year, month)
    if await state.get_state() == AdminStat.start_date:
        key = await inline.concatenate_key(
            calendar_key,
            await inline.back('admin_menu')
        )
    else:
        key = await inline.concatenate_key(
            calendar_key,
            await inline.pass_key(),
            await inline.back('admin_menu')
        )
    await call.message.edit_reply_markup(reply_markup=key)
    await state.update_data({'year': year, 'month': month})


@dp.callback_query(cf.DateCall.filter(), AdminStat.start_date)
async def statistics_start_date(call: types.CallbackQuery,
                                state: FSMContext,
                                callback_data: cf.DateCall):
    start_date: date = date(year=callback_data.year,
                            month=callback_data.month,
                            day=callback_data.day)
    current_date: date = date.today()
    text = 'Укажите конец для вывода статистики либо пропустите, чтоб получить статистику по 1 дню: '
    key = await inline.concatenate_key(
        await inline.calendar_keyboard(current_date.year, current_date.month),
        await inline.pass_key(),
        await inline.back('admin_menu')
    )
    try:
        await call.message.edit_text(text, reply_markup=key)
    except Exception as e:
        log.warning(e)
        await call.message.answer(text, reply_markup=key)
    await state.set_state(AdminStat.end_date)
    await state.update_data({'start_date': start_date})


@dp.callback_query(cf.DateCall.filter(), AdminStat.end_date)
async def statistics_start_date(call: types.CallbackQuery,
                                state: FSMContext,
                                callback_data: cf.DateCall,
                                config: Config):
    data = await state.get_data()
    start_date: date = data.get('start_date', date.today())
    end_date: date = date(year=callback_data.year,
                          month=callback_data.month,
                          day=callback_data.day)
    if end_date <= start_date:
        await call.answer('Конечная дата не может быть меньше начальной!',
                          show_alert=True)
    else:
        await answer_statistics_message(
            call.message,
            datetime(start_date.year, start_date.month, start_date.day, config.misc.start_work),
            datetime(end_date.year, end_date.month, end_date.day, config.misc.start_work)
        )


@dp.callback_query(cf.ActionCall.filter(
    F.action == 'pass'
), AdminStat.end_date)
async def statistics_pass_second_date(call: types.CallbackQuery,
                                      state: FSMContext,
                                      config: Config):
    data = await state.get_data()
    start_date: date = data.get('start_date', date.today())
    end_date: date = start_date + timedelta(days=1)
    await answer_statistics_message(
        call.message,
        datetime(start_date.year, start_date.month, start_date.day, config.misc.start_work),
        datetime(end_date.year, end_date.month, end_date.day, config.misc.start_work)
    )


async def answer_statistics_message(message: types.Message,
                                    start_date: datetime,
                                    end_date: datetime):
    key = await inline.back('admin_menu')
    text = get_text_for_statistics(start_date, end_date)
    try:
        await message.edit_text(text, reply_markup=key)
    except Exception as e:
        log.warning(e)
        await message.answer(text, reply_markup=key)
